{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 对用户进行聚类\n",
    "根据用户的属性进行聚类，深试K=20, 40, 80, 并计算各自的CH_scores."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# import tool kit\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.cluster import MiniBatchKMeans\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn import metrics\n",
    "\n",
    "from sklearn.decomposition import PCA\n",
    "import time\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 读取数据\n",
    "train = pd.read_csv('users.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user_id</th>\n",
       "      <th>locale</th>\n",
       "      <th>birthyear</th>\n",
       "      <th>gender</th>\n",
       "      <th>joinedAt</th>\n",
       "      <th>location</th>\n",
       "      <th>timezone</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>3197468391</td>\n",
       "      <td>id_ID</td>\n",
       "      <td>1993</td>\n",
       "      <td>male</td>\n",
       "      <td>2012-10-02T06:40:55.524Z</td>\n",
       "      <td>Medan  Indonesia</td>\n",
       "      <td>480.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>3537982273</td>\n",
       "      <td>id_ID</td>\n",
       "      <td>1992</td>\n",
       "      <td>male</td>\n",
       "      <td>2012-09-29T18:03:12.111Z</td>\n",
       "      <td>Medan  Indonesia</td>\n",
       "      <td>420.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>823183725</td>\n",
       "      <td>en_US</td>\n",
       "      <td>1975</td>\n",
       "      <td>male</td>\n",
       "      <td>2012-10-06T03:14:07.149Z</td>\n",
       "      <td>Stratford  Ontario</td>\n",
       "      <td>-240.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1872223848</td>\n",
       "      <td>en_US</td>\n",
       "      <td>1991</td>\n",
       "      <td>female</td>\n",
       "      <td>2012-11-04T08:59:43.783Z</td>\n",
       "      <td>Tehran  Iran</td>\n",
       "      <td>210.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>3429017717</td>\n",
       "      <td>id_ID</td>\n",
       "      <td>1995</td>\n",
       "      <td>female</td>\n",
       "      <td>2012-09-10T16:06:53.132Z</td>\n",
       "      <td>NaN</td>\n",
       "      <td>420.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      user_id locale birthyear  gender                  joinedAt  \\\n",
       "0  3197468391  id_ID      1993    male  2012-10-02T06:40:55.524Z   \n",
       "1  3537982273  id_ID      1992    male  2012-09-29T18:03:12.111Z   \n",
       "2   823183725  en_US      1975    male  2012-10-06T03:14:07.149Z   \n",
       "3  1872223848  en_US      1991  female  2012-11-04T08:59:43.783Z   \n",
       "4  3429017717  id_ID      1995  female  2012-09-10T16:06:53.132Z   \n",
       "\n",
       "             location  timezone  \n",
       "0    Medan  Indonesia     480.0  \n",
       "1    Medan  Indonesia     420.0  \n",
       "2  Stratford  Ontario    -240.0  \n",
       "3        Tehran  Iran     210.0  \n",
       "4                 NaN     420.0  "
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user_id</th>\n",
       "      <th>timezone</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>3.820900e+04</td>\n",
       "      <td>37773.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>2.150982e+09</td>\n",
       "      <td>110.161226</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>1.242134e+09</td>\n",
       "      <td>359.604823</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>6.110000e+03</td>\n",
       "      <td>-720.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>1.072041e+09</td>\n",
       "      <td>-240.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>2.153423e+09</td>\n",
       "      <td>240.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>3.222394e+09</td>\n",
       "      <td>420.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>4.294808e+09</td>\n",
       "      <td>840.000000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "            user_id      timezone\n",
       "count  3.820900e+04  37773.000000\n",
       "mean   2.150982e+09    110.161226\n",
       "std    1.242134e+09    359.604823\n",
       "min    6.110000e+03   -720.000000\n",
       "25%    1.072041e+09   -240.000000\n",
       "50%    2.153423e+09    240.000000\n",
       "75%    3.222394e+09    420.000000\n",
       "max    4.294808e+09    840.000000"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train.describe()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 38209 entries, 0 to 38208\n",
      "Data columns (total 7 columns):\n",
      "user_id      38209 non-null int64\n",
      "locale       38209 non-null object\n",
      "birthyear    38209 non-null object\n",
      "gender       38100 non-null object\n",
      "joinedAt     38152 non-null object\n",
      "location     32745 non-null object\n",
      "timezone     37773 non-null float64\n",
      "dtypes: float64(1), int64(1), object(5)\n",
      "memory usage: 2.0+ MB\n"
     ]
    }
   ],
   "source": [
    "train.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数据存在int类型、object、和float类型，并且目前没有空值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "38209"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n_records = train.shape[0]\n",
    "n_records"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 统计下有多少不同的users\n",
    "def get_uniqueUsers():\n",
    "    uniqueUsers = set()\n",
    "    \n",
    "    for i in range(n_records):\n",
    "        uniqueUsers.add(train.loc[i, 'user_id'])\n",
    "        \n",
    "    n_events = len(uniqueUsers)\n",
    "    return n_events\n",
    "\n",
    "n_users = get_uniqueUsers()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "38209"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n_users"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "目前没有重复的用户"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>locale</th>\n",
       "      <th>birthyear</th>\n",
       "      <th>gender</th>\n",
       "      <th>joinedAt</th>\n",
       "      <th>timezone</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>id_ID</td>\n",
       "      <td>1993</td>\n",
       "      <td>male</td>\n",
       "      <td>2012-10-02T06:40:55.524Z</td>\n",
       "      <td>480.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>id_ID</td>\n",
       "      <td>1992</td>\n",
       "      <td>male</td>\n",
       "      <td>2012-09-29T18:03:12.111Z</td>\n",
       "      <td>420.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>en_US</td>\n",
       "      <td>1975</td>\n",
       "      <td>male</td>\n",
       "      <td>2012-10-06T03:14:07.149Z</td>\n",
       "      <td>-240.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>en_US</td>\n",
       "      <td>1991</td>\n",
       "      <td>female</td>\n",
       "      <td>2012-11-04T08:59:43.783Z</td>\n",
       "      <td>210.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>id_ID</td>\n",
       "      <td>1995</td>\n",
       "      <td>female</td>\n",
       "      <td>2012-09-10T16:06:53.132Z</td>\n",
       "      <td>420.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  locale birthyear  gender                  joinedAt  timezone\n",
       "0  id_ID      1993    male  2012-10-02T06:40:55.524Z     480.0\n",
       "1  id_ID      1992    male  2012-09-29T18:03:12.111Z     420.0\n",
       "2  en_US      1975    male  2012-10-06T03:14:07.149Z    -240.0\n",
       "3  en_US      1991  female  2012-11-04T08:59:43.783Z     210.0\n",
       "4  id_ID      1995  female  2012-09-10T16:06:53.132Z     420.0"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 去掉不需要属性\n",
    "train = train.drop(['user_id'], axis=1)\n",
    "\n",
    "#location 有NaN，抛弃\n",
    "train = train.drop(['location'], axis=1)\n",
    "train.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 特征工程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 特征编码处理\n",
    "#特征编码\n",
    "import datetime\n",
    "import hashlib\n",
    "import locale\n",
    "\n",
    "from collections import defaultdict\n",
    "from sklearn.preprocessing import normalize\n",
    "\n",
    "class FeatureEng:\n",
    "    def __init__(self):\n",
    "         # 载入 locales\n",
    "        self.localeIdMap = defaultdict(int)\n",
    "        for i, l in enumerate(locale.locale_alias.keys()):\n",
    "          self.localeIdMap[l] = i + 1\n",
    "        \n",
    "        # 载入 gender id 字典\n",
    "        ##缺失补0\n",
    "        self.genderIdMap = defaultdict(int, {'NaN': 0, \"male\":1, \"female\":2})\n",
    "\n",
    "  \n",
    "    def getLocaleId(self, locstr):\n",
    "        return self.localeIdMap[locstr.lower()]\n",
    "\n",
    "    def getGenderId(self, genderStr):\n",
    "        return self.genderIdMap[genderStr]\n",
    "\n",
    "    def getJoinedYearMonth(self, dateString):\n",
    "        try:\n",
    "            dttm = datetime.datetime.strptime(dateString, \"%Y-%m-%dT%H:%M:%S.%fZ\")\n",
    "            #return \"\".join([str(dttm.year), str(dttm.month)])\n",
    "            return (dttm.year-2010)*12 + dttm.month\n",
    "        except:  #缺失补0\n",
    "          return 0\n",
    "\n",
    "    def getBirthYearInt(self, birthYear):\n",
    "        #缺失补0\n",
    "        try:\n",
    "          return 0 if birthYear == \"None\" else int(birthYear)\n",
    "        except:\n",
    "          return 0\n",
    "\n",
    "    def getTimezoneInt(self, timezone):\n",
    "        try:\n",
    "          return int(timezone)\n",
    "        except:  #缺失值处理\n",
    "          return 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "FE = FeatureEng()\n",
    "\n",
    "cols = ['LocaleId', 'BirthYearInt', 'GenderId', 'JoinedYearMonth', 'TimezoneInt']\n",
    "n_cols = len(cols)\n",
    "userMatrix = np.zeros((train.shape[0],n_cols), dtype=np.int)\n",
    "\n",
    "for i in range(train.shape[0]): \n",
    "    userMatrix[i, 0] = FE.getLocaleId(train.loc[i,'locale'])\n",
    "    userMatrix[i, 1] = FE.getBirthYearInt(train.loc[i,'birthyear'])\n",
    "    userMatrix[i, 2] = FE.getGenderId(train.loc[i,'gender'])\n",
    "    userMatrix[i, 3] = FE.getJoinedYearMonth(train.loc[i,'joinedAt'])\n",
    "    #userMatrix[i, 4] = FE.getCountryId(df[''])\n",
    "    userMatrix[i, 4] = FE.getTimezoneInt(train.loc[i,'timezone'])\n",
    "\n",
    "# 归一化用户矩阵\n",
    "userMatrix = normalize(userMatrix, norm=\"l1\", axis=0, copy=False)\n",
    "\n",
    "df_FE = pd.DataFrame(data=userMatrix, columns=cols)  \n",
    "#mmwrite(\"US_userMatrix\", userMatrix)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>LocaleId</th>\n",
       "      <th>BirthYearInt</th>\n",
       "      <th>GenderId</th>\n",
       "      <th>JoinedYearMonth</th>\n",
       "      <th>TimezoneInt</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.000036</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000019</td>\n",
       "      <td>0.000026</td>\n",
       "      <td>0.000036</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.000036</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000019</td>\n",
       "      <td>0.000026</td>\n",
       "      <td>0.000031</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0.000020</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000019</td>\n",
       "      <td>0.000026</td>\n",
       "      <td>-0.000018</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.000020</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000038</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000016</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.000036</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000038</td>\n",
       "      <td>0.000026</td>\n",
       "      <td>0.000031</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   LocaleId  BirthYearInt  GenderId  JoinedYearMonth  TimezoneInt\n",
       "0  0.000036      0.000027  0.000019         0.000026     0.000036\n",
       "1  0.000036      0.000027  0.000019         0.000026     0.000031\n",
       "2  0.000020      0.000027  0.000019         0.000026    -0.000018\n",
       "3  0.000020      0.000027  0.000038         0.000027     0.000016\n",
       "4  0.000036      0.000027  0.000038         0.000026     0.000031"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_FE.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(38209, 5)\n"
     ]
    }
   ],
   "source": [
    "print(df_FE.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 聚类并计算CH_socres"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 进行聚类\n",
    "def K_cluster_analysis(K, X_train):\n",
    "    start = time.time()\n",
    "    print(\"K-means begin with clusters: {}\".format(K))\n",
    "    \n",
    "    #K-means, 在训练集上训练\n",
    "    mb_kmeans = MiniBatchKMeans(n_clusters=K)\n",
    "    mb_kmeans.fit(X_train)\n",
    "    \n",
    "    # 在训练集上测试, 计算得分\n",
    "    CH_score = metrics.silhouette_score(X_train, mb_kmeans.predict(X_train))\n",
    "    \n",
    "    end = time.time()\n",
    "    print(\"CH_score: {}, time elaps:{}\".format(CH_score, int(end-start)))\n",
    "    \n",
    "    return CH_score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "K-means begin with clusters: 20\n",
      "CH_score: 0.6732829169885896, time elaps:7834\n",
      "K-means begin with clusters: 40\n",
      "CH_score: 0.6507398445742587, time elaps:2339\n",
      "K-means begin with clusters: 80\n",
      "CH_score: 0.6573012329713853, time elaps:2311\n"
     ]
    }
   ],
   "source": [
    "# 设置超参数（K）,搜索范围\n",
    "Ks = [20, 40, 80]\n",
    "CH_scores = []\n",
    "for K in Ks:\n",
    "    ch = K_cluster_analysis(K, df_FE)\n",
    "    CH_scores.append(ch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 绘制模型性能"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x1a18ee4b70>]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a1858d400>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 绘制不同PCA维数下模型的性能，找到最佳模型／参数（分数最高）\n",
    "plt.plot(Ks, np.array(CH_scores), 'b-')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "K为20的CH_socre的得分比较高。以20为K，进行结果输出"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 输出预测结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "MiniBatchKMeans(batch_size=100, compute_labels=True, init='k-means++',\n",
       "        init_size=None, max_iter=100, max_no_improvement=10, n_clusters=20,\n",
       "        n_init=3, random_state=None, reassignment_ratio=0.01, tol=0.0,\n",
       "        verbose=0)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n_clusters = 20\n",
    "km = MiniBatchKMeans(n_clusters=n_clusters)\n",
    "km.fit(df_FE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "df_FE['cluster_20'] = km.predict(df_FE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "df_FE.to_csv('user_FE.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>LocaleId</th>\n",
       "      <th>BirthYearInt</th>\n",
       "      <th>GenderId</th>\n",
       "      <th>JoinedYearMonth</th>\n",
       "      <th>TimezoneInt</th>\n",
       "      <th>cluster_20</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.000036</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000019</td>\n",
       "      <td>0.000026</td>\n",
       "      <td>0.000036</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.000036</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000019</td>\n",
       "      <td>0.000026</td>\n",
       "      <td>0.000031</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0.000020</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000019</td>\n",
       "      <td>0.000026</td>\n",
       "      <td>-0.000018</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.000020</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000038</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000016</td>\n",
       "      <td>13</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.000036</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>0.000038</td>\n",
       "      <td>0.000026</td>\n",
       "      <td>0.000031</td>\n",
       "      <td>11</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   LocaleId  BirthYearInt  GenderId  JoinedYearMonth  TimezoneInt  cluster_20\n",
       "0  0.000036      0.000027  0.000019         0.000026     0.000036           3\n",
       "1  0.000036      0.000027  0.000019         0.000026     0.000031           3\n",
       "2  0.000020      0.000027  0.000019         0.000026    -0.000018           6\n",
       "3  0.000020      0.000027  0.000038         0.000027     0.000016          13\n",
       "4  0.000036      0.000027  0.000038         0.000026     0.000031          11"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_FE.head()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
